<?php
PHP面向对象
===============================================
    header("content-type:text/html;charset=utf-8");
  

        对象：客观存在的任何一个物体
        类：  对对象的一个描述
        先有类  然后  通过类 实例化对象

        面向过程的编程：基于步骤
        面向对象的编程：基于对象

        三大优势：重用性   扩展性   灵活性

        三大特性：封装   继承   多态

//////////////////////////////////////////////////    关键字与方法      //////////////////////////////////////////////////////////////

    //关键字
    /class new          public protected private                        static      final       instanceof          const       extends         clone
      final                   //可以修饰类和方法          被修饰的方法不能被重写   被修饰的类不能被继承
 
      instanceof         //可以认为是一个运算符   判断左边的对象是不是右边的类的实例化

      static                 //可以修饰属性和方法        访问方式使用范围操作符，不再通过对象访问
                                //类名::$静态属性名              类名::静态方法()
        self            // self是指向类本身，也就是self是不指向任何已经实例化的对象，一般self使用来指向类中的静态变量。     

        extends            // 类的继承
                                //class A{}
                                //class B extends A{}       类B继承了A
        interface   implements          //接口
        abstract        抽象类

    // 魔术方法  （自动调用）
            __construct             //实类化类的时候自动执行
            __destruct               //实类化类的结束时候最后一个执行的程序

            __set($key,$val)     protected private  //没有权限操作的时候调用此方法
            __get($key)             protected private  //没有权限操作的时候调用此方法
            __isset($key)
            __unset($val)

            __sleep            serialize    串行化    将对象转化为字符串
            __wakeup        unerialize      饭串行化    将已经转化为对象的字符串转化为对象

            //注：只能调用方法
            __call （$a（方法名）,$b（数组））               //调用一个不存在的       （方法 ）     时自动调用
            __callStatic （$a（方法名）,$b（数组））       //调用一个不存在的   （静态方法）  时自动调用

           
            __toString （）         //返回值必须是一个字符串 用于一个类被当成字符串时应怎样回应

            __clone （）        clone    //被克隆的与原来的对象是同一个对象

            __autoload （待加载的类$class）     //实例化对象时自动加载        （主要是加载未定义的类）


实例：
 __construct
 __destruct         
构造方法 __construct与析构方法 __destruct 的实例；
//先进后出,后进先出。类似于瓶子放东西的道理。
 class test{
    public function __construct($name){
        $this->name=$name;
        echo "{$this->name}被构造。。。<br>";
    }
    public function __destruct(){
        echo "{$this->name}被析构。。。<br>";
    }
}

$t1 = new test("A");
$t2 = new test("B");
$t3 = new test('C');
$t4 = new test('D');
执行结果：
                A被构造。。。
                B被构造。。。
                C被构造。。。
                D被构造。。。
                D被析构。。。
                C被析构。。。
                B被析构。。。
                A被析构。。。

class A{
    public function __construct(){
        echo "class A....";
    }
}
class B extends A{
    public function __construct(){
        parent::__construct();
        echo "class B ....";
    }
}
$b = new B;
结果：class A....class B ....
-----------------------------------------------------------------------------------------
__set（$key,$val）  protected private  //没有权限操作的时候调用此方法,键值操作
__get（$key）         protected private  //没有权限操作的时候调用此方法，键操作


class base{
    protected $age = 18;
    private $name = '张三';

    public function __set($key,$val){   //参数：键和值
        $this->$key = $val;             //键的赋值
        return '设置'.$key.'的值是'.$val;
    }

    public function __get($key){    //参数：键
        return '获取的键是'.$key.'得到的值是'.$this->$key;
    }
}

$base = new base;
$base->age = 20;   //__set($key,$val)的键值设置
echo $base->age;

echo '<br>';

echo $base->name;       //__get($key),值得获取
---------------------------------------------------------------------------------------------
__isset（$key,$val）          isset
__unset（$val）
__isset( $property ) 当在一个未定义的属性上调用isset()函数时调用此方法
__unset( $property ) 当在一个未定义的属性上调用unset()函数时调用此方法
与__get方法和__set方法相同，这里的没有声明包括当使用对象调用时，访问控制为proteced,private的属性（即没有权限访问的属性）
class A
{
    public $name = 'aaaaaaaaaaaaa';
    protected $age = 18;
    private = 'B';

    public function __isset($key){
        return isset($this->$key);
    }
}
$a = new A;
if(isset($a->aaa)){
    echo 'Y';
}else{
    echo 'N';
}
---------------------------------------------------------------------------------------
__sleep             serialize        串行化        将对象转化为字符串
__wakeup        unerialize      反串行化     将已经转化为对象的字符串转化为对象
            class B
            {
                public $name = 'xxxxxxx';
                public $age = 18;
                public $sex = 'aaaaaaaaa';

                public function __sleep(){
                    return array('sex','age');          //反回一个数组，数组的元素（值）就是允许被串行化的属性
                }

                public function __wakeup(){
                    echo '<br>aaaaaaaa<br>';
                }
            }

            $b = new B;

            $str = serialize($b);
            echo $str;

            $obj = unserialize($str);
            var_dump($obj);

--------------------------------------------------------------------------------
__call （$a（方法名）,$b（数组））               //调用一个不存在的       （方法 ）     时自动调用
__callStatic （$a（方法名）,$b（数组））       //调用一个不存在的   （静态方法）  时自动调用
//注：只能调用方法

    class A
    {
        public function __call($a,$b){
            echo '调用不存在的    方法      是自动调用';
        }
    }

    class B
    {
        public static function __callStatic($a,$b){
            echo '动用不存在的        静态方法        是自动调用';
        }
    }

    $a = new A;
    $a->say('属性名','数组');        //不存在的      方法      会自动调用  __call  方法
    $a->say();                               //可以没有参数

    B::say('属性名','数组');          //不存在的      静态方法      会自动调用  __callStatic  方法
    B::say();                                //可以没有参数
--------------------------------------------------------------------------------
__toString      //用于一个类被当成字符串时应怎样回应
    class A
    {
        public function __toString(){
            return '必须返回一个字符串';         //返回值必须是一个字符串
        }
    }

    $a = new A;
    echo $a;

--------------------------------------------------------------------------------
__clone     clone

                class A
                {
                    public function __clone(){
                        echo '__clone克隆!!!!!!!<br>';
                    }
                }

                $a = new A;
                $b = clone $a;              //需要使用  clone   关键字

                if($a == $b){     //不能使用===判断，因为是两个对象，可以使用instanceof来判断
                    echo '$a,$b两个是同一个对象';       //被克隆的与原来的对象是同一个对象
                }else{
                    echo '$a,$b两个不是同一个对象';
                }
--------------------------------------------------------------------------------
__autoload （待自动加载的类）     //实例化对象时自动加载（主要是加载类）   
单独写出来不在类中实现。
//__autoload 不是写在类中，而是写在类的外面
function __autoload($classname){        //$classname 必须参数，待加载的类名。
    $classpath = './'.$classname.'.class.php';  //加载类的文件路径
    if(file_exists($classpath)){
        include_once("$classpath");          //在有继承的情况下之加载一次。
    }else{
        echo '不存在';
    }
}
$a = new user;      //当被实例化时自动加载的类。

user.class.php     类文件
class user{
    public function __construct(){
        echo '我是自动加载的类';
    }
}
/////////////////////////////////////////////////////////   范围操作符       ////////////////////////////////////////

            静态访问符    ::             成员操作符    ->                 parent  父类                  self     自己            $this   当前对象

            $this      对象    当前对象       使用的时候只能在类里面的方法里面
            parent     类      父类
            self       类      当前类

范围操作符：
            静态方法   A::show();
            静态属性   A::$属性名
            常量          A::常量名


class A
{
    public $name;
    public $age;
    public $sex;

    public function __construct($name,$age,$sex){
        $this->name = $name;
        $this->age = $age;
        $this->sex = $sex;
    }
}

class B extends A{
    public $size;

    public function __donstruct($name,$age,$sex,$size){
        parent::__construct($name,$age,$sex);  
       //或者  A::__construct($name,$age,$sex);  
        $this->size = $size;
    }
}

$b = new B('name','age','sex','size');
var_dump($b);

---------------------------------------------------------------------------------------
//static 静态方法与属性的访问不是通过对象访问，而是通过操作符 ::
//作用：   无论实例化了多少个对象，static定义的属性和方法只有一个。
class A{
    public static $name = '我的名字';
    public static function one(){
        // echo A::$name;    这两个输出是相同的意思，调用自己。
        echo self::$name;   //  操作符是 ::    
    }
}
// 可以不用实例化
A::one();           //访问静态方法的访问中间的操作符  ::
echo A::$name;      //访问静态属性。
----------------------------------------------
 final关键字，修饰方法和类
//final只能修饰的类和方法
//被修饰的类不能被继承，被修饰的方法不能重写。
//final 不能修饰属性
class A{   
     // final $name;     final 关键字不能修饰属性
     final public function one(){       //final修饰的方法
        echo 'aaaaaaaaaaaaaaaaaa';
    }
}

final class B{} //final要写在 class 的前面

-------------------------------------------------------------------------------------------------
/*
        define 定义常量，可以使用表达式，不能写在类里面
        const  定义常量，不能使用表达式，可以写在类里面

        类里面常量的访问方式，和静态一致，使用范围操作符来访问  如：  A::USER;
*/
//////////////////////////////////////////////////////     面向对象的封装：///////////////////////////////////////////////////
            
                                public           protected           private
        外面                     Yes                  NO                  NO
        相关的类              Yes                  Yes                  NO
        自己                     Yes                  Yes                  Yes

        yes 支持    no 不支持
///////////////////////////////////////////////////      面向对象的继承    /////////////////////////////////////
extends 继承
/*        
            描述：
                    php的继承只能是单继承（只能继承一个类）   但是可以被多个类继承
                    php的继承可以是多层继承    C继承B    B继承A    那么C里面就有了ABC三个类里面的所有东西

            属性的继承：
                   （可见属性 public protected ） 在子类中出现同名的属性，会被重写
                    （不可见属性 private） 在子类中出现同名的属性，会产生一个新的属性，互不影响

            方法的继承：
                    （可见属性 public protected ） 在子类中出现同名的方法，会被重写
                    （不可见属性 private） 在子类中出现同名的方法，会产生一个新的方法，互不影响
                    使用父类中的东西，可以使用范围操作符  parent::父类方法
*/
class A{
    public $name = '属性';
    public function one(){
        echo 'oneoneonoenoneoneone';
    }
}
class B extends A{
    public function one(){
        parent::one();  //重写的父类方法，
        echo '自己外加的';
    }
}
$b = new B();
$b->one();
///////////////////////////////////////////    单态设计模式：（最终只能得到一个对象）   ////////////////////////////////
       
class A{
    public static $link = null;

    public function __construct(){
        echo '链接数据库';
    }
    public static function getconnect(){
        if(is_null(self::$link)){
            return self::$link = new A;
        }else{
            return self::$link;
        }
    }
}
$a = A::getconnect();
echo '<br>';
$b =  A::getconnect();
if($a === $b){
    echo 'Y';
}else{
    echo 'N';
}

结果：  //证明这是同一个对象。
链接数据库
Y
////////////////////////   多态设计模式：（不同对象通过同一个类产生不同结果） ////////////////////////////

    class A{
    public function aa($obj){
        $obj->eat();
        echo '<br>';
        $obj->say();
    }
}

interface B{
    public function eat();
    public function say();
}

class Ba implements B{
    public function eat(){
        echo 'BaBaBaBaBaBaeateateateateateateat';
    }
    public function say(){
        echo 'BaBaBaBaBaBasaysaysaysaysaysaysaysaysaysaysaysaysay';
    }
}

class Bb implements B{
    public function eat(){
        echo 'BbBbBbBbBbBbeateateateateateateateateateat';
    }

    public function say(){
        echo 'BbBbBbBbBbBbsaysaysaysaysaysaysaysayssaysaysaysay';
    }
}

$a = new A;
$Ba = new Ba;
$Bb = new Bb;

$a->aa($Ba);
echo '<hr>';
$a->aa($Bb);



////////////////////////////////////////////     抽象类       /////////////////////////////////////////////////

 abstract    抽象类 
 作用：抽象类不实现具体方法，具体方法由子类完成。
//定义抽象类 abstract
abstract class A{
    //abstract 定义抽象类的方法，这里没有花括号。子类必须实现这个抽象方法。
    abstract public function say();

    //抽象类可以有参数
    abstract public function eat($argument);
    //在抽象类中可以定义普通的方法。
    
    public function run(){
        echo '这是run方法';
    }
}
class B extends A{
    //子类必须实现父类的抽象方法，否则是致命的错误。
    public function say(){
        echo '这是say方法,实现了抽象方法';
    }
    public function eat($argument){
        echo '抽象类可以有参数 ，输出参数：'.$argument;
    }
}
$b =new B;
$b->say();
echo '<br>';
$b->eat('apple');
echo '<br>';
$b->run();

////////////////////////////////////////////     接口       /////////////////////////////////////////////////

// interface 接口 implements 实现接口    是单继承 ，多实现。作用：定义规范。

interface  A{   //接口 没有 class 关键字。
    const AA = '接口可以有常量';       //接口中可以有常量，但是没有变量
    public function atest();    //接口方法没有修饰词，不能有（花括号）方法体。
}
interface B{
    public function btest();
}
interface C{
    public function ctest($argument);   //接口方法可以有参数。
}
//接口可以多实现，单继承
class D implements A,B,C{
    public function atest(){}       //每个接口的方法都必须实现。
    public function btest(){}
    public function ctest($argument){
        echo '这是ctest方法';
    }
}
echo A::AA;     //常量的访问。
echo '<br>';
$d = new D;
$d->ctest('参数');


//接口可以单继承，多实现。
class E{
    public function etest(){}
}
class F extends E implements A,B,C{ //extends 与 implements 顺序不能写反
    public function atest(){}
    public function btest(){}
    public function ctest($argument){}
}